use crate::{mapper, utils::amap};
use chrono::NaiveDateTime;
use dotenv as env;
use rust_decimal::Decimal;
use sea_orm::{
    sqlx, ActiveModelBehavior, ActiveModelTrait, ActiveValue, ColIdx, ConnectionTrait,
    DatabaseBackend, DatabaseConnection, IntoActiveModel, QueryResult, Statement, TryIntoModel,
};
use serde_json::json;
use sky_common::constant::{self, order};
use sky_pojo::{
    convert_field, convert_option_field,
    dto::{
        self,
        order::{
            OrderCancelDTO, OrderConfirmDTO, OrderPaymentVO, OrderRejectionDTO, OrdersPageQueryDTO,
        },
    },
    entities::{
        self, order_detail,
        orders::{self, ActiveModel},
        shopping_cart,
    },
    vo::{
        order::{OrderStatisticsVO, OrderSubmitVO, OrderVO},
        PageResult,
    },
};
use std::collections::HashMap;
// use crate::{mapper, utils::wechat_payment::WeChatPayUtil};

pub async fn submit_order(
    db: &DatabaseConnection,
    dto: dto::order::OrdersSubmitDTO,
    user_id: i64,
) -> Result<OrderSubmitVO, String> {
    let address_book = mapper::address_book::get_by_id(db, dto.address_book_id)
        .await
        .map_err(|e| e.to_string())?;

    match check_out_of_range(address_book.detail.clone().unwrap()).await {
        Ok(_) => {}
        Err(e) => return Err(e),
    }

    let mut shopping_cart = shopping_cart::ActiveModel::new();
    shopping_cart.user_id = ActiveValue::set(user_id);

    let shopping_cart_list = mapper::shoppingcart::list(db.clone(), shopping_cart)
        .await
        .map_err(|e| e.to_string())?;

    if shopping_cart_list.is_empty() || shopping_cart_list.len() == 0 {
        return Err(constant::message::SHOPPING_CART_IS_NULL.to_string());
    };

    let mut order = ActiveModel::from(dto);
    order.phone = ActiveValue::set(Some(address_book.phone));
    order.address = ActiveValue::set(address_book.detail);

    order.consignee = ActiveValue::set(address_book.consignee);
    order.number = ActiveValue::set(Some(chrono::Utc::now().timestamp_millis().to_string()));
    order.user_id = ActiveValue::set(user_id);
    order.status = ActiveValue::set(constant::order::PENDING_PAYMENT);
    order.pay_status = ActiveValue::set(constant::order::UN_PAID as i8);
    order.order_time = ActiveValue::set(chrono::Utc::now().naive_local());

    let order_result = mapper::order::insert(&db.clone(), order.clone())
        .await
        .map_err(|e| e.to_string())?;

    let mut order_detail_list: Vec<order_detail::ActiveModel> = Vec::new();

    for _cart in shopping_cart_list {
        let active_model = order_detail::ActiveModel {
            name: ActiveValue::set(_cart.name),
            order_id: ActiveValue::set(Some(order_result.id)),
            dish_id: ActiveValue::set(_cart.dish_id),

            setmeal_id: ActiveValue::set(_cart.setmeal_id),
            number: ActiveValue::set(_cart.number),
            amount: ActiveValue::set(_cart.amount),
            ..Default::default()
        };

        order_detail_list.push(active_model);
    }
    mapper::order::order_detail_insert_batch(db, order_detail_list)
        .await
        .unwrap();

    let _ = mapper::shoppingcart::delete_by_user_id(db, user_id)
        .await
        .unwrap();

    let vo = OrderSubmitVO {
        id: order_result.id,
        order_number: order_result.number.unwrap(),
        order_amount: order_result.amount,
        order_time: order_result.order_time,
    };

    Ok(vo)
}

pub async fn payment(
    db: &DatabaseConnection,
    dto: dto::order::OrderPaymentDTO,
    user_id: i64,
) -> Result<serde_json::Value, String> {
    let user = match mapper::user::get_by_id(db.clone(), user_id).await {
        Ok(user) => user,
        Err(e) => return Err(format!("Error fetching user: {}", e).into()),
    };

    // 检查 openid 是否存在
    let _openid = match user.unwrap().openid {
        Some(openid) => openid,
        None => {
            return Err(
                sea_orm::JsonValue::String("User openid is missing".to_string()).to_string(),
            )
        }
    };

    // let pay_util = WeChatPayUtil::new();

    // let json_obj = match pay_util
    //     .pay(
    //         &dto.order_number,
    //         Decimal::new(1, 2),
    //         "苍穹外卖订单",
    //         &openid,
    //     )
    //     .await
    // {
    //     Ok(obj) => obj,
    //     Err(e) => return Err(format!("支付失败： ${}", e.1)),
    // };

    // if json_obj.get("code").is_some() && json_obj.get("code").unwrap().eq("ORDERPAID") {
    //     return Err("该订单已支付".to_string());
    // }

    // let vo_val: serde_json::Value =
    //     serde_json::to_value(json_obj.clone()).map_err(|e| e.to_string())?;

    // let mut vo: OrderPaymentVO = serde_json::from_value(vo_val).map_err(|e| e.to_string())?;
    // vo.package_str = json_obj.get("package").unwrap().to_string();
    let vo = json!({
        "code": "200",
        "message": "success"
    });
    match pay_success(db, dto.order_number.to_string(), user_id).await {
        Ok(_) => return Ok(vo),
        Err(e) => return Err(e),
    }
}

pub async fn pay_success(
    db: &DatabaseConnection,
    out_trade_no: String,
    user_id: i64,
) -> Result<(), String> {
    let order_db = match mapper::order::get_by_number_and_user_id(db, out_trade_no, user_id)
        .await
        .map_err(|e| e.to_string())
        .unwrap()
    {
        Some(data) => data,
        None => return Err("Not Found".to_string()),
    };
    let order = entities::orders::ActiveModel {
        id: ActiveValue::set(order_db.id),
        status: ActiveValue::set(constant::order::TO_BE_CONFIRMED),
        pay_status: ActiveValue::set(constant::order::PAID.try_into().unwrap()),
        checkout_time: ActiveValue::set(Some(chrono::Utc::now().naive_local())),
        ..Default::default()
    };

    let _ = match mapper::order::update(db, order_db.id.to_string(), order).await {
        Ok(_) => return Ok(()),
        Err(e) => return Err(e),
    };
}

pub async fn page_query4_user(
    db: &DatabaseConnection,
    page_dto: dto::order::OrderPageQueryDTO,
    user_id: i64,
) -> Result<PageResult<OrderVO>, String> {
    let dto = dto::order::OrdersPageQueryDTO {
        page: page_dto.page,
        page_size: page_dto.page_size,
        number: None,
        phone: None,
        status: page_dto.status,
        begin_time: None,
        end_time: None,
        user_id: Some(user_id),
    };

    let vec = match mapper::order::page_query(db, dto).await {
        Ok(vec) => vec,
        Err(e) => return Err(e.to_string()),
    };
    let total = vec.len();

    let mut list: Vec<OrderVO> = Vec::new();

    for order in vec {
        let order_id: i64 = order.id;

        let order_dishes = match mapper::order::order_detail_get_by_order_id(db, order_id).await {
            Ok(vec) => vec,
            Err(e) => return Err(e.to_string()),
        };

        // tracing::debug!("{:?}", order);
        // tracing::debug!("{:?}", order_dishes);

        list.push(OrderVO {
            order_dishes,
            order_detail_list: order,
        });
    }

    Ok(PageResult {
        total: total.try_into().unwrap(),
        records: list,
    })
}

pub async fn details(db: &DatabaseConnection, id: i64) -> Result<OrderVO, String> {
    let order_detail_list = mapper::order::get_by_id(db, id)
        .await
        .map_err(|e| e.to_string())
        .unwrap()
        .unwrap();

    let order_dishes = mapper::order::order_detail_get_by_order_id(db, id)
        .await
        .map_err(|e| e.to_string())?;

    Ok(OrderVO {
        order_dishes,
        order_detail_list,
    })
}

pub async fn user_cancel_by_id(db: &DatabaseConnection, id: i64) -> Result<(), String> {
    let order_db = match mapper::order::get_by_id(db, id).await {
        Ok(model) => model.unwrap(),
        Err(e) => return Err(e.to_string()),
    };

    if order_db.status > 2 {
        return Err(constant::message::ORDER_STATUS_ERROR.to_owned());
    };

    let mut order = orders::ActiveModel::new();

    order.id = ActiveValue::set(order_db.id);

    if order_db.status.eq(&constant::order::TO_BE_CONFIRMED) {
        // let pay_util = WeChatPayUtil::new();
        // pay_util
        //     .refund(
        //         &order_db.clone().number.unwrap(),
        //         &order_db.clone().number.unwrap(),
        //         Decimal::new(1, 2),
        //         Decimal::new(1, 2),
        //     )
        //     .await;

        order.pay_status = ActiveValue::set(constant::order::REFUND as i8);
    }

    Ok(())
}

fn convert_order_to_cart(
    x: entities::order_detail::Model,
    user_id: i64,
) -> entities::shopping_cart::ActiveModel {
    entities::shopping_cart::ActiveModel {
        id: ActiveValue::NotSet,
        name: ActiveValue::set(x.name),
        image: ActiveValue::set(x.image),
        user_id: ActiveValue::set(user_id),
        dish_id: ActiveValue::set(x.dish_id),
        setmeal_id: ActiveValue::set(x.setmeal_id),
        dish_flavor: ActiveValue::set(x.dish_flavor),
        number: ActiveValue::set(x.number),
        amount: ActiveValue::set(x.amount),
        create_time: ActiveValue::NotSet,
    }
}

pub async fn repetition(
    db: &DatabaseConnection,
    id: i64,
    user_id: i64,
) -> Result<sea_orm::InsertResult<shopping_cart::ActiveModel>, String> {
    let order_detail_list = mapper::order::order_detail_get_by_order_id(db, id)
        .await
        .unwrap();

    let mut shopping_cart_list: Vec<entities::shopping_cart::ActiveModel> = Vec::new();

    for x in order_detail_list {
        let shopping_cart = convert_order_to_cart(x, user_id);
        let model = shopping_cart.try_into_model().unwrap();
        shopping_cart_list.push(model.into());
    }

    let result = mapper::shoppingcart::insert_batch(db, shopping_cart_list)
        .await
        .map_err(|e| e.to_string());

    result
}

// for admin

pub async fn condition_search(
    db: &DatabaseConnection,
    dto: OrdersPageQueryDTO,
) -> Result<PageResult<OrderVO>, String> {
    let vec = mapper::order::page_query(db, dto)
        .await
        .map_err(|e| e.to_string())?;

    let ordervo_list = get_order_vo_vec(db, vec.clone()).await;

    Ok(PageResult {
        total: vec.len() as u64,
        records: ordervo_list,
    })
}

// for condition_search
async fn get_order_vo_vec(
    db: &DatabaseConnection,
    order_vec: Vec<entities::orders::Model>,
) -> Vec<OrderVO> {
    let mut order_vo_vec: Vec<OrderVO> = Vec::new();
    if !order_vec.is_empty() {
        for model in order_vec {
            let order_dishes = get_order_dishes_str(db, model.clone()).await;
            let order_vo = OrderVO {
                order_dishes,
                order_detail_list: model,
            };
            order_vo_vec.push(order_vo);
        }
    }

    order_vo_vec
}
// for get_order_vo_vec
async fn get_order_dishes_str(
    db: &DatabaseConnection,
    order: entities::orders::Model,
) -> Vec<entities::order_detail::Model> {
    let order_detail_vec = mapper::order::order_detail_get_by_order_id(db, order.id)
        .await
        .unwrap();

    order_detail_vec
        .into_iter() // 获取所有权
        .map(|mut x| {
            x.name = Some(format!("{}*{};", x.name.unwrap_or_default(), x.number));
            x
        })
        .collect()
}

pub async fn statistics(db: &DatabaseConnection) -> Result<OrderStatisticsVO, String> {
    // 使用orm
    // let to_be_confirmed = mapper::order::count_status(db, constant::order::TO_BE_CONFIRMED)
    //     .await
    //     .map_err(|e| e.to_string())?;

    // let confirmed = mapper::order::count_status(db, constant::order::CONFIRMED)
    //     .await
    //     .map_err(|e| e.to_string())?;

    // let delivery_in_progress =
    //     mapper::order::count_status(db, constant::order::DELIVERY_IN_PROGRESS)
    //         .await
    //         .map_err(|e| e.to_string())?;

    // 使用视图
    let result: QueryResult = db
        .query_one(Statement::from_string(
            DatabaseBackend::MySql,
            "select to_be_confirmed, confirmed, delivery_in_progress from order_statistics;",
        ))
        .await
        .map_err(|e| e.to_string())?
        .unwrap();

    let to_be_confirmed: Option<u64> = result.try_get("", "to_be_confirmed").unwrap_or(None);
    let confirmed: Option<u64> = result.try_get("", "confirmed").unwrap_or(None);
    let delivery_in_progress: Option<u64> =
        result.try_get("", "delivery_in_progress").unwrap_or(None);

    Ok(OrderStatisticsVO {
        to_be_confirmed,
        confirmed,
        delivery_in_progress,
    })
}

pub async fn confirm(db: &DatabaseConnection, dto: OrderConfirmDTO) -> Result<(), String> {
    let model = entities::orders::ActiveModel {
        number: ActiveValue::set(Some(dto.id.to_string())),
        status: ActiveValue::set(constant::order::CONFIRMED),
        ..Default::default()
    };

    mapper::order::update(db, dto.id.to_string(), model).await
}

pub async fn rejection(db: &DatabaseConnection, dto: OrderRejectionDTO) -> Result<(), &str> {
    let order_info = mapper::order::get_by_id(db, dto.id)
        .await
        .map_err(|e| e.to_string())
        .unwrap()
        .unwrap();

    if order_info.status.eq(&constant::order::TO_BE_CONFIRMED) {
        return Err(constant::message::ORDER_STATUS_ERROR);
    }

    if order_info.status.eq(&constant::order::PAID) {
        // refund to do
        tracing::info!("申请退款：{}", "refund info");
    }

    let mut rej_order = entities::orders::ActiveModel::new();
    rej_order.id = ActiveValue::set(dto.id);
    rej_order.status = ActiveValue::set(constant::order::CANCELLED);
    rej_order.cancel_time = ActiveValue::set(Some(chrono::Utc::now().naive_utc()));
    rej_order.rejection_reason = ActiveValue::set(Some(dto.rejection_reason));

    let _ = mapper::order::update(db, dto.id.to_string(), rej_order).await;

    Ok(())
}

pub async fn cancel(db: &DatabaseConnection, dto: OrderCancelDTO) -> Result<(), String> {
    let order_info = mapper::order::get_by_id(db, dto.id)
        .await
        .map_err(|e| e.to_string())
        .unwrap()
        .unwrap();

    if order_info.status.eq(&constant::order::PAID) {
        // cancel to do
        tracing::info!("申请退款：{}", "cancel info");
    }

    let mut cancel_order = entities::orders::ActiveModel::new();
    cancel_order.id = ActiveValue::set(dto.id);
    cancel_order.status = ActiveValue::set(constant::order::CANCELLED);
    cancel_order.cancel_time = ActiveValue::set(Some(chrono::Utc::now().naive_utc()));
    cancel_order.rejection_reason = ActiveValue::set(Some(dto.cancel_reason));

    let _ = mapper::order::update(db, dto.id.to_string(), cancel_order).await;

    Ok(())
}

pub async fn delivery(db: &DatabaseConnection, id: i64) -> Result<(), String> {
    let res = mapper::order::get_by_id(db, id).await;

    let order_info = match res {
        Ok(v) => match v {
            Some(a) => a,
            None => return Err(constant::message::ORDER_STATUS_ERROR.to_string()),
        },
        Err(e) => return Err(e.to_string()),
    };

    if !order_info.status.eq(&constant::order::CONFIRMED) {
        return Err(constant::message::ORDER_STATUS_ERROR.to_string());
    }

    let mut order = entities::orders::ActiveModel::new();
    order.number = ActiveValue::set(Some(id.to_string()));
    order.status = ActiveValue::set(constant::order::DELIVERY_IN_PROGRESS);

    let _ = mapper::order::update(db, id.to_string(), order).await;

    Ok(())
}

pub async fn complete(db: &DatabaseConnection, id: i64) -> Result<(), String> {
    let order_info = mapper::order::get_by_id(db, id)
        .await
        .map_err(|e| e.to_string())
        .unwrap()
        .unwrap();

    if !order_info.status.eq(&constant::order::DELIVERY_IN_PROGRESS) {
        return Err(constant::message::ORDER_STATUS_ERROR.to_string());
    }

    let mut order = entities::orders::ActiveModel::new();
    order.number = ActiveValue::set(Some(id.to_string()));
    order.status = ActiveValue::set(constant::order::COMPLETED);
    order.delivery_time = ActiveValue::set(Some(chrono::Utc::now().naive_utc()));

    let _ = mapper::order::update(db, id.to_string(), order).await;

    Ok(())
}

pub async fn check_out_of_range(address: String) -> Result<bool, String> {
    dotenv::dotenv().ok();

    let shop_add = env::var("shop.address").unwrap();

    let src_lat_lng = amap::get_longitude_and_latitude(shop_add).await.unwrap();
    let dest_lat_lng = amap::get_longitude_and_latitude(address).await.unwrap();

    let distance = amap::directionlite(src_lat_lng, dest_lat_lng)
        .await
        .unwrap();

    Ok(distance > 5_000)
}

pub async fn reminder(db: &DatabaseConnection, id: i64) -> Result<(), String> {
    let order = match mapper::order::get_by_id(db, id.clone()).await {
        Ok(order) => order,
        Err(e) => return Err(e.to_string()),
    };

    if order.is_none() {
        return Err(constant::message::ORDER_NOT_FOUND.to_string());
    }

    let order_id = format!("{}", id);
    let mut map = HashMap::<&str, &str>::new();
    map.insert("type", "2");
    map.insert("orderId", &order_id);
    map.insert("content", &format!("订单号：{:?}", order.unwrap().number));

    Ok(())
}

pub async fn status_eq(db: &DatabaseConnection, id: String, status: i32) -> Result<bool, String> {
    match mapper::order::get_status_by_id(db, id).await {
        Ok(v) => return Ok(v[0] == status),
        Err(e) => {
            tracing::error!("{}", e.to_string());
            return Err(constant::message::ORDER_NOT_FOUND.to_string());
        }
    };
}
